<?php

namespace kitego\services\common;

use think\facade\Cache;

class LockService
{
    /**
     * 执行阻塞锁
     * @param $key
     * @param $fn
     * @param int $ex
     * @return mixed
     */
    public static function exec($key, $fn, int $ex = 6): mixed
    {
        try {
            self::lock($key, $key, $ex);

            return $fn();
        } finally {
            self::unlock($key, $key);
        }
    }

    /**
     * 递归加锁
     */
    public static function lock($key, string $value = '1', int $ex = 6)
    {
        if (self::tryLock($key, $value, $ex)) {
            return true;
        }

        usleep(200);

        self::lock($key, $value, $ex);
    }

    /**
     * 解锁
     */
    public static function unlock($key, string $value = '1'): bool
    {
        $script = <<<EOF
if (redis.call("get", "lock_" .. KEYS[1]) == ARGV[1]) then
	return redis.call("del", "lock_" .. KEYS[1])
else
	return 0
end
EOF;
        return Cache::store('redis')->handler()->eval($script, [$key, $value], 1) > 0;
    }

    /**
     * 立即返回锁
     * @param $key
     * @param string $value
     * @param int $ex
     * @return mixed
     */
    public static function tryLock($key, string $value = '1', int $ex = 6): mixed
    {
        return Cache::store('redis')->handler()->set('lock_' . $key, $value, ["NX", "EX" => $ex]);
    }
}